package com.nbf.DataManager;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

import org.apache.log4j.Logger;

import com.nbf.Collections.SecurityInfo;
import com.nbf.Session.PortfolioSession;
import com.nbf.Util.StringExt;

/** The DataManager loads, stores, and sorts all of the chosen market data<br>
 *  At this time, everything is loaded into memory due to the low granularity of the provided data<br>
 * 
 * @author Zachary David
 *
 */
public class DataManager
{
	private Logger 									_logger 			= Logger.getLogger(getClass());
	private Map<String, File> 						_availableDirs = new HashMap<String, File>();
	private Map<String, SecurityInfo>			_allSymbols;
	private List<String> 							_allMarketVariableTypes;
	private List<Long> 								_allAvailableDates; 
	private Map<Long, List<SecurityInfo>> 		_dateToSecurity;
	
	public DataManager(String dataDir)
	{
		File rootDir = new File(dataDir);
		if(!rootDir.isDirectory())
		{
			logError(StringExt.make("Data Directory:",dataDir,"is not a directory"));
			return;
		}
		
		for(File file : rootDir.listFiles())
		{
			_availableDirs.put(file.getName(), file);
			sendConsoleMessage(StringExt.make(file.getName(),"data available"));
		}
	}
	
	/** Instructs the DataManager to search through the chosen directory
	 * and populate the available Symbols, MarketVariables, and DataPacks
	 * 
	 * @param dir selected granularity type
	 */
	public void populateDataManager(String dir)
	{
		File chosenDir = _availableDirs.get(dir);
		
		if(chosenDir == null)
		{
			logError(StringExt.make(dir,"not available!"));
			return;
		}
		
		_allSymbols					= new HashMap<String, SecurityInfo>();
		_allMarketVariableTypes = new ArrayList<String>();
		_allAvailableDates 		= new ArrayList<Long>();
		_dateToSecurity			= new HashMap<Long, List<SecurityInfo>>();
		
		boolean allOk = true;
		for(File file : chosenDir.listFiles())
		{
			if(!buildSymbol(file))
				allOk = false;
		}
		
		if(!allOk)
			sendConsoleMessage(StringExt.make("Data Populated With Some Errors. Check Log"));
		
	}
	
	/** This is a big confusing mess. But it does what it needs to do.
	 * 
	 * @param dir
	 * @return whether or not the symbol was correctly populated without any collisions. 
	 */
	private boolean buildSymbol(File dir)
	{
		boolean builtOk = true;
		try
		{
			String symbol = dir.getName();
			if(_allSymbols.containsKey(symbol))
			{
				logError(StringExt.make(symbol,"already exists!"));
				return false;
			}
			
			SecurityInfo securityInfo = new SecurityInfo(symbol);
			_allSymbols.put(symbol, securityInfo);
			
			for(File file : dir.listFiles())
			{
				Scanner scanner = new Scanner(file);
				String[] header = scanner.nextLine().split(",");
				HashMap<Integer, String> variables = new HashMap<Integer, String>();
				for(int i = 1; i < header.length; i++)
				{
					String varName = header[i];
					if(!_allMarketVariableTypes.contains(varName))
						_allMarketVariableTypes.add(varName);
					variables.put(i, varName);
				}
				
				while(scanner.hasNext())
				{
					String[] line = scanner.nextLine().split(",");
					Long date = Long.parseLong(line[0]);
					if(!_allAvailableDates.contains(date))
						_allAvailableDates.add(date);
					List<SecurityInfo> securities = _dateToSecurity.get(date);
					if(securities == null)
						securities = new ArrayList<SecurityInfo>();
					if(!securities.contains(securityInfo))
						securities.add(securityInfo);
					
					for(Entry<Integer, String> entry : variables.entrySet())
					{
						if(!securityInfo.tryAdd(date, entry.getValue(), Double.parseDouble(line[entry.getKey()])))
						{
							builtOk = false;
							logError(StringExt.make("Collision occured",date,entry.getValue()));
						}
					}
				}
			}
		}
		catch (Exception e)
		{
			logError(StringExt.make("error parsing",dir.getName()));
			logError(e);
			builtOk = false;
		}
		finally
		{
			
		}
		return builtOk;
	}
	
	/**
	 * Used for sending available dates to user
	 * @return all available dates
	 */
	public Long[] getAllAvailableDates()
	{
		return _allAvailableDates.toArray(new Long[_allAvailableDates.size()]);
	}
	
	/**
	 * Used for sending available variable types to user
	 * @return all available market types
	 */
	public String[] getAllAvailableVariables()
	{
		return _allMarketVariableTypes.toArray(new String[_allMarketVariableTypes.size()]);
	}
	
	private void logError(Object obj)
	{
		_logger.error(obj);
		sendConsoleMessage(obj);
	}
	
	private void sendConsoleMessage(Object obj)
	{
		if(PortfolioSession.CONSOLE_ENABLED)
			System.out.println(obj);
	}
}
